/** @file
@brief Contains declarations related to #PrynComponent. */
#ifndef PRYN_COMPONENT_H
#define PRYN_COMPONENT_H

typedef struct PrynComponent PrynComponent; /// A component in a graph.

typedef PrynDList (PrynComponent) PrynComponentList; ///< List of components.

#include <pryn/factory.h>
#include <pryn/monitor.h>
#include <pryn/object.h>
#include <pryn/pin.h>
#include <pryn/state.h>
#include <pryn/tag.h>
#include <pryn/utility.h>

/** @defgroup PrynComponent PrynComponent C API
	@{ */

PrynImport (PrynResult, prynComponentCreate, (PrynComponent **component, PrynFactory *factory))
	/**< Create a component from the factory.

	@param factory The factory to use to create the component. This will attempt to load the factory if it's unloaded.
	@param component Receives the newly-created component, or 0 if there was some kind of error.
	@returns #PrynResultSuccess on success, or an error code on failure; see #PrynResult. */

PrynImport (PrynResult, prynComponentAllocate, (PrynComponent **component, PrynFactory *factory, size_t proprietarySize))
	/**< @brief Allocate a component and clear it to normal defaults.

	This is called by factories, not clients. Use #prynComponentCreate to create a component using the factory.

	@param factory The factory to assign to the component.
	@param component Receives the newly-allocated component, or 0 if there was some kind of error.
	@param proprietarySize Number of bytes to allot to the proprietary data or 0 to use none. If non-zero this data is after the end of the component.
	@returns #PrynResultSuccess on success, or an error code on failure; see #PrynResult. */

PrynImport (PrynResult, prynComponentDestroy, (PrynComponent *component))
	/**< @brief Completely destroy the component, freeing all memory.

	@param component The component to destroy. */

PrynImport (PrynResult, prynComponentDisassociate, (PrynComponent *component))
	/**< @brief Detach the component from the factory.

	This causes all pins to be cut. All functions using this component will return errors, except for #prynComponentDestroy. This is used when a factory is being destroyed.

	@param component The component to disassociate. */

PrynImport (PrynResult, prynComponentToObject, (PrynComponent *component, PrynObject *object))
	/**< @brief Convert the component into an object.

	@param component The component to convert.
	@param[out] object Written with the component object or an empty object on failure.
	@returns #PrynResultSuccess or an error code; see #PrynResult. */

/** @name Properties
	@{ */

PrynImport (PrynResult, prynComponentState, (PrynComponent *component, PrynState **output)) ///< Retrieve the state for a component.
PrynImport (PrynResult, prynComponentFactory, (PrynComponent *component, PrynFactory **output)) ///< Retrieve the factory for the component. This is 0 if the component has been disassociated; see #prynComponentDisassociate.
PrynImport (PrynResult, prynComponentName, (PrynComponent *component, PrynString *output)) ///< Retrieve the name of the component. The value is duplicated off.
PrynImport (PrynResult, prynComponentDescription, (PrynComponent *component, PrynString *output)) ///< Retrieve the description of the component. The value is duplicated off.
PrynImport (PrynResult, prynComponentProprietary, (PrynComponent *component, void **output)) ///< Retrieve the proprietary pointer for the component.
PrynImport (PrynResult, prynComponentTags, (PrynComponent *component, PrynTagList **output)) ///< Retrieve the list of tags associated with the component.
PrynImport (PrynResult, prynComponentMonitors, (PrynComponent *component, PrynMonitorList **output)) ///< Retrieve the list of monitors associated with the component.
PrynImport (PrynResult, prynComponentDisplayName, (PrynComponent *component, PrynString *output)) ///< Retrieve the name to display to an editor for the component. You do not need to free the string. The string is valid until the next call to #prynComponentDisplayName on this component, or until the component is destroyed.
PrynImport (PrynResult, prynComponentOutputPins, (PrynComponent *component, PrynPinList **output)) ///< Retrieve the output pins of the component.
PrynImport (PrynResult, prynComponentInputPins, (PrynComponent *component, PrynPinList **output)) ///< Retrieve the input pins of the component.
PrynImport (PrynResult, prynComponentFirstOutputPin, (PrynComponent *component, PrynPin **pin)) ///< Retrieve the first output pin or return #PrynResultFalse if there are no output pins.

/** @} */ // (group properties)

PrynImport (PrynResult, prynComponentIterateOutputPins, (PrynComponent *component, PrynPin **pin))
	/**< @brief Iterate over the output pins of the component.

	This is used like this:

@code
PrynPin *component = ...; // Set to a valid component.
PrynPin *pin;

for (pin = 0; !prynIterateComponentOutputPins (component, &pin); )
	// Process this pin.
@endcode

	@param component The component whose pins should be iterated over.
	@param[in, out] pin If *pin is zero, then this will be written with the first output pin if any; otherwise it's written with the next output pin after *pin. 0 is written on error or if there are no more pins.
	@returns #PrynResultSuccess if there was a pin, #PrynResultDone if iteration has completed. This may also return an error code; see #PrynResult. */

PrynImport (PrynResult, prynComponentIterateInputPins, (PrynComponent *component, PrynPin **pin))
	/**< @brief Iterate over the input pins of the component.

	This is used like this:

@code
PrynPin *component = ...; // Set to a valid component.
PrynPin *pin;

for (pin = 0; !prynIterateComponentInputPins (component, &pin); )
	// Process this pin.
@endcode

	@param component The component whose pins should be iterated over.
	@param[in, out] pin If *pin is zero, then this will be written with the first input pin if any; otherwise it's written with the next input pin after *pin. 0 is written on error or if there are no more pins.
	@returns #PrynResultSuccess if there was a pin, #PrynResultDone if iteration has completed. This may also return an error code; see #PrynResult. */


PrynImport (PrynResult, prynComponentClick, (PrynComponent *component))
	/**< @brief Click on the component.

	This is used by the editor when the user clicks on an editor component. This may have an effect, or it may not.

	@param component The component to click on.
	@returns #PrynResultSuccess on success, #PrynResultDone if no action was performed. This may return an error code; see #PrynResult. */

#ifdef PrynInternalStructs
/** @brief A component in a graph.
	@ingroup PrynComponent */
struct PrynComponent
{
#ifdef PrynInternal 
/** @name Internal fields
	These fields are not normally visible to clients, and it is recommended not to use them to maximise binary compatibility. To make them available, define PrynInternal.
	@{ */

	PrynCommonObject pCommonObject; ///< Common fields you'd expect in an object, like tags and monitors.
	PrynCommonResource pCommonResource; ///< Common fields you'd expect in a resource, like name and description.

	PrynComponent *pNext; ///< Next component using the factory or 0 if this is last.
	PrynComponent *pPrevious; ///< Previous component using this factory or 0 if this is first.

	PrynState *pState; ///< State of the original factory that created this component.
	PrynFactory *pFactory; ///< Factory that created this component, or 0 if this is disassociated.
	void *pProprietary; ///< Proprietary information used by the factory.
	
	PrynPinList pInputPins; ///< Input pins.
	PrynPinList pOutputPins; ///< Output pins.
	

/** @} */
#endif /* PrynInternal */

#if __cplusplus
	static inline PrynComponent *Create (PrynFactory *factory) { PrynComponent *result; factory->checkError (prynComponentCreate (&result, factory)); return result; }
		/**< Create a component from the factory.

		@param factory The factory to use to create the component. This will load the factory if it's unloaded.
		@returns The newly-created component. */

	static inline PrynComponent *Allocate (PrynFactory *factory, size_t proprietarySize) { PrynComponent *result; factory->checkError (prynComponentAllocate (&result, factory, proprietarySize)); return result; }
		/**< @brief Allocate a component and clear it to normal defaults.

		This is called by factories, not clients. Use #Create to create a component using the factory.

		@param factory The factory to assign to the component.
		@param proprietarySize Number of bytes to allot to the proprietary data or 0 to use none. If non-zero this data is after the end of the component.
		@returns The newly-allocated component. */

	inline void destroy () { prynComponentDestroy (this); }
		/**< @brief Completely destroy the component, freeing all memory. This will not throw an exception. */

	inline void disassociate () { prynComponentDisassociate (this); }
		/**< @brief Detach the component from the factory.

		This causes all pins to be cut. Most functions using this component will throw exceptions, except for #destroy. This is used when a factory is being destroyed. This will not throw an exception. */

	inline PrynObject toObject () { PrynObject result; checkError (prynComponentToObject (this, &result)); return result; }
		/**< @brief Convert the component into an object.

		@returns An object with type #PrynObjectTypeComponent. */

	/** @name Properties
		@{ */

	inline PrynState *state () { PrynState *result; prynCheckErrorNull (prynComponentState (this, &result)); return result; } ///< Return the state for the component.
	inline PrynFactory *factory () { PrynFactory *result; checkError (prynComponentFactory (this, &result)); return result; } ///< Return the factory for the component. This is 0 if the component has been disassociated; see #disassociate.
	inline PrynPinList *inputPins () { PrynPinList *result; checkError (prynComponentInputPins (this, &result)); return result; } ///< List of all the input pins for the component.
	inline PrynPinList *outputPins () { PrynPinList *result; checkError (prynComponentOutputPins (this, &result)); return result; } ///< List of all the output pins for the component.
	inline PrynString name () { PrynString name; checkError (prynComponentName (this, &name)); return name; } ///< Name of the component.
	inline PrynString description () { PrynString result; checkError (prynComponentDescription (this, &result)); return result; } ///< Description of the component.
	inline PrynString displayName () { PrynString result; checkError (prynComponentDisplayName (this, &result)); return result; } ///< The name to display to an editor for the component. You do not need to free the string. The string is valid until the next call to #prynComponentDisplayName on this component, or until the component is destroyed.
	inline void *proprietary () { void *result; checkError (prynComponentProprietary (this, &result)); return result; } ///< Proprietary pointer for the component or 0 if there's no proprietary data.
	inline PrynTagList *tags () { PrynTagList *result; checkError (prynComponentTags (this, &result)); return result; } ///< List of tags associated with the component or 0 if component is 0.
	inline PrynMonitorList *monitors () { PrynMonitorList *result; checkError (prynComponentMonitors (this, &result)); return result; } ///< List of monitors associated with the component.

	inline PrynPin *firstOutputPin () { PrynPin *result; checkError (prynComponentFirstOutputPin (this, &result)); return result; } ///< Return the first output pin in the component or 0 if there are none.

	/** @} */ // (group properties)

	inline PrynPin *createOutputPin (const PrynString &id, const PrynString &name, const PrynString &description) { PrynPin *result; checkError (prynPinCreateOutput (&result, this, &id, &name, &description)); return result; }
		/**< Create a new output pin on the component, putting it at the end.

		@param id Optionally an identifier for a pin. This does not need to be unique. The value is adopted, not copied.
		@param name The name of the pin. The value is adopted, not copied.
		@param description The description of the pin. The value is adopted, not copied.
		@returns The new pin. */

	inline PrynPin *createInputPin (const PrynString &id, const PrynString &name, const PrynString &description) { PrynPin *result; checkError (prynPinCreateInput (&result, this, &id, &name, &description)); return result; }
		/**< Create a new input pin on the component, putting it at the end.

		@param id Optionally an identifier for a pin. This does not need to be unique. The value is adopted, not copied.
		@param name The name of the pin. The value is adopted, not copied.
		@param description The description of the pin. The value is adopted, not copied.
		@returns The new pin. */

	inline bool iterateOutputPins (PrynPin **pin) { return PrynIsTrue (checkError (prynComponentIterateOutputPins (this, pin))); }
		/**< @brief Iterate over the output pins of the component.

		This is used like this:

@code
PrynPin &component = ...; // Set to a valid component.
PrynPin *pin = 0;

while (component.iterateOutputPins (&pin))
	// Process this pin.
@endcode

		@param[in, out] pin If *pin is zero, then this will be written with the first output pin if any; otherwise it's written with the next output pin after *pin. 0 is written on error or if there are no more pins.
		@returns @b true if there is a pin; @b false if iteration is complete. */

	inline bool iterateInputPins (PrynPin **pin) { return PrynIsTrue (checkError (prynComponentIterateInputPins (this, pin))); }
		/**< @brief Iterate over the input pins of the component.

		This is used like this:

@code
PrynPin *component = ...; // Set to a valid component.
PrynPin *pin = 0;

while (component.iterateInputPins (&pin))
	// Process this pin.
@endcode

		@param[in, out] pin If *pin is zero, then this will be written with the first input pin if any; otherwise it's written with the next input pin after *pin. 0 is written on error or if there are no more pins.
		@returns @b true if there is a pin; @b false if iteration is complete. */

	inline bool click () { return PrynIsTrue (checkError (prynComponentClick (this))); }
		/**< @brief Click on the component.

		This is used by the editor when the user clicks on an editor component. This may have an effect, or it may not.

		@returns @b true if an action was performed, @b false otherwise. */

	inline PrynResult checkError (PrynResult result) { return prynCheckErrorBase (state (), result); } ///< If the result is below zero, throw an error.

#endif /* __cplusplus */
};
#endif /* PrynInternalStructs */

/** @} */ // (end of group PrynComponent)

#endif /* PRYN_COMPONENT_H */
